unit uNetworkInterface;

interface

uses Dialogs, ScktComp, SysUtils,
     uMessageCodes, uBusMessage, uRailroadYard;

const
  BYTES_PER_MESSAGE_CODE = 1;

type

  TNetworkInterface = class
    // attributes
    private
      aRailroadYard: TRailroadYard;
      aSocket: TServerSocket;
      aHandlerMap: array[1..MAX_MESSAGE_CODE]
                      of procedure(Socket: TCustomWinSocket; paMessage: TBusMessage) of object;

    // methods
    public
      constructor Create(paPort: integer; paRailRoad: TRailroadYard);

    protected
      procedure Start();
      procedure InitMessageHandlers();
      procedure OnReadData(Sender: TObject; paSocket: TCustomWinSocket);
      procedure SendMessage(paSocket: TCustomWinSocket; paMessage: TBusMessage);

      procedure OnClientError(Sender: TObject; Socket: TCustomWinSocket;
                              ErrorEvent: TErrorEvent; var ErrorCode: Integer);

      // message handlers
      procedure OnUnknown(paSocket: TCustomWinSocket; paMessage: TBusMessage);
      procedure OnPing(paSocket: TCustomWinSocket; paMessage: TBusMessage);
      procedure OnClearanceRequest(paSocket: TCustomWinSocket; paMessage: TBusMessage);
  end;



implementation

constructor TNetworkInterface.Create(paPort: integer; paRailRoad: TRailroadYard);
begin
  aSocket := TServerSocket.create(nil);
  aSocket.Port := paPort;
  aSocket.OnClientError := OnClientError;

  aRailroadYard := paRailRoad;
  InitMessageHandlers();
  Start();
end;


procedure TNetworkInterface.Start;
begin
  aSocket.Active := true;

  aSocket.OnClientRead := OnReadData;
end;


procedure TNetworkInterface.InitMessageHandlers;
var
  i: integer;
begin
  for i := 1 to MAX_MESSAGE_CODE do
  begin
    aHandlerMap[i] := OnUnknown;
  end;

  aHandlerMap[IN_PING] := OnPing;
  aHandlerMap[IN_SEGMENT_CLEARANCE_REQUEST] := OnClearanceRequest;
end;


procedure TNetworkInterface.OnReadData(Sender: TObject; paSocket: TCustomWinSocket);
var
  data: string;
  message: TBusMessage;
  opcode: integer;

begin
  message.length := paSocket.ReceiveLength;
  paSocket.ReceiveBuf(message.data[0], message.length);
  opcode := message.data[0];

  if (opcode > 0) and (opcode <= MAX_MESSAGE_CODE) then
    aHandlerMap[opcode](paSocket, message);
end;


procedure TNetworkInterface.SendMessage(paSocket: TCustomWinSocket; paMessage: TBusMessage);
begin
  paSocket.SendBuf(paMessage.data, paMessage.length);
end;


procedure TNetworkInterface.OnClientError(Sender: TObject; Socket: TCustomWinSocket;
                                          ErrorEvent: TErrorEvent; var ErrorCode: Integer);
begin
  ErrorCode := 0;
end;


// message handlers

procedure TNetworkInterface.OnUnknown(paSocket: TCustomWinSocket; paMessage: TBusMessage);
var
  message: TBusMessage;
begin
  message.length := 1;
  message.data[0] := 0;
  SendMessage(paSocket, message); // send empty response
end;


procedure TNetworkInterface.OnPing(paSocket: TCustomWinSocket; paMessage: TBusMessage);
var
  message: TBusMessage;
begin
  message.length := 1;
  message.data[0] := OUT_PING;
  SendMessage(paSocket, message);
end;


procedure TNetworkInterface.OnClearanceRequest(paSocket: TCustomWinSocket; paMessage: TBusMessage);
var
  response: TBusMessage;
  mask: integer;
  i: integer;
  occupied : boolean;
begin
  mask := paMessage.data[1];

  response.data[0] := OUT_SEGMENT_CLEARANCE_RESPONSE;
  response.data[1] := 0;

  for i := 0 to 7 do
  begin
    if (mask and (1 shl i)) <> 0 then
    begin
      if (aRailroadYard.IsSegmentOccupied(i)) then
        response.data[1] := response.data[1] or (1 shl i);
    end;

  end;

  response.length := 2;
  SendMessage(paSocket, response);

end;


end.

